package com.business.common.utils;

import com.business.common.config.FileUploadConfig;
import com.business.common.enums.BusinessExceptionEnum;
import com.business.common.exception.BusinessException;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.exception.MultiObjectDeleteException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import static com.qcloud.cos.exception.MultiObjectDeleteException.DeleteError;
import static com.qcloud.cos.model.DeleteObjectsRequest.KeyVersion;
import static com.qcloud.cos.model.DeleteObjectsResult.DeletedObject;

/**
 * name=fenzhidao-1257402134, creationDate=Wed Sep 25 14:00:36 CST 2019, location=ap-guangzhou, owner=COSOwner [name=100007120468,id=qcs::cam::uin/100007120468:uin/100007120468
 */
@Component
public class FileUploadUtil {

    private static final Logger log = LoggerFactory.getLogger(FileUploadUtil.class);


    /**
     * 获取文件夹
     *
     * @return
     */
    private String getDirectory() {
        Calendar calendar = Calendar.getInstance();
        StringBuilder stringBuilder = new StringBuilder(FileUploadConfig.FILE_DIRECTORY);
        stringBuilder.append(calendar.get(Calendar.YEAR)).append("/").append(calendar.get(Calendar.MONTH) + 1).append("/").append(calendar.get(Calendar.DATE)).append("/");
        return stringBuilder.toString();
    }

    /**
     * 初始化上传
     *
     * @return
     */

    public COSClient initUpload() {
        // 1 初始化用户身份信息（secretId, secretKey）
        COSCredentials cred = new BasicCOSCredentials(FileUploadConfig.SECRET_ID, FileUploadConfig.SECRET_KEY);
        // 2 设置 bucket 的区域,
        Region region = new Region("ap-guangzhou");
        ClientConfig clientConfig = new ClientConfig(region);
        // 3 生成 cos 客户端。
        COSClient cosClient = new COSClient(cred, clientConfig);
        return cosClient;
    }

    /**
     * 上传图片
     *
     * @param fileUpload
     * @return
     * @throws IOException
     */
    public String uploadImage(MultipartFile fileUpload) throws IOException {
        if (fileUpload == null) {
            throw new BusinessException(BusinessExceptionEnum.FILE_READING_ERROR);
        }
        if (!FileUtil.validateFileSize(fileUpload, 10)) {//图片大小限制
            throw new BusinessException(BusinessExceptionEnum.OVER_SIZE_FILE);
        }
        if (FileUtil.getType(fileUpload) == null) {//类型不对
            throw new BusinessException(BusinessExceptionEnum.IMAGE_TYPE_ERROR);
        }
        String contentType = fileUpload.getContentType();
        String originalFilename = fileUpload.getOriginalFilename();
        contentType = contentType == null || contentType.equals("") ? FileUploadConfig.DEFAULT_FILE_TYPE : contentType;

        String fileUrl = uploadFile(FileUtil.renameToUUID(originalFilename), contentType, fileUpload.getInputStream());
        return fileUrl;
    }

    /**
     * 上传文件
     *
     * @param fileName 文件名称 需要携带目录  eg: a/b/abc.png
     * @param fileType 文件类型 image/png  请明确那种上传类型.
     * @param stream   流数据
     * @return
     */
    public String uploadFile(String fileName, String fileType, InputStream stream) {
        COSClient cosClient = initUpload();
        String url = "";
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            // 设置输入流长度为500
            objectMetadata.setContentLength(stream.available());//加快文件获取速度
            log.info("流的长度为==={},", stream.available());
            objectMetadata.setContentType(fileType);
            log.info("文件类型为====>{}", fileType);
            String dir = getDirectory();
            PutObjectResult putObjectResult = cosClient.putObject(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, dir + fileName, stream, objectMetadata);
            log.info("Etag===>{}", putObjectResult.getETag());
            url = getUrl(fileName, dir, cosClient);
        } catch (IOException e) {
            e.printStackTrace();
            return url;
        } catch (CosClientException clientException) {
            clientException.printStackTrace();
            return url;
        } finally {
            cosClient.shutdown();
        }
        return url;
    }


    /**
     * 分片上传
     *
     * @param fileName   文件名称
     * @param partStream 流
     * @param partNumber 分片数量
     * @param partSize   分片大小
     * @return url
     */
    public String uploadFragmentationFile(String fileName, InputStream partStream, int partNumber, int partSize) {
        COSClient cosClient = initUpload();
        String url = "";
        try {
            String dir = getDirectory() + fileName;
            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, dir);
            InitiateMultipartUploadResult initResponse = cosClient.initiateMultipartUpload(initRequest);
            String uploadId = initResponse.getUploadId();
            // 上传分块, 最多10000个分块, 分块大小支持为1M - 5G.
            // 分块大小设置为4M. 如果总计 n 个分块, 则 1~n-1 的分块大小一致, 最后一块小于等于前面的分块大小
            List<PartETag> partETags = new ArrayList<>();
            // partStream 代表 part 数据的输入流, 流长度为 partSize
            UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO).
                    withUploadId(uploadId).withKey(dir).withPartNumber(partNumber).
                    withInputStream(partStream).withPartSize(partSize);
            UploadPartResult uploadPartResult = cosClient.uploadPart(uploadRequest);
            String eTag = uploadPartResult.getETag();  // 获取 part 的 Etag
            log.info("eTag上传信息为:=====>{}", eTag);
            partETags.add(new PartETag(partNumber, eTag));  // partETags 记录所有已上传的 part 的 Etag 信息
            log.info("上次进度信息====>{}", partETags);
            CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, dir, uploadId, partETags);
            cosClient.completeMultipartUpload(compRequest);
            url = getUrl(fileName, dir, cosClient);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            cosClient.shutdown();
        }
        return url;

    }

    public String getUrl(String fileUrl, String dir, COSClient cosClient) {
        LocalDateTime localDateTime = LocalDateTime.now().plusYears(20);
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        GeneratePresignedUrlRequest req =
                new GeneratePresignedUrlRequest(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, dir + fileUrl, HttpMethodName.GET);
        req.setExpiration(Date.from(zdt.toInstant()));
        URL url = cosClient.generatePresignedUrl(req);
        if (url == null) {
            throw new BusinessException(BusinessExceptionEnum.URL_NOT_NULL);
        }
        return url.toString();
    }

    public String getUrl(String fileUrl) {
        if (fileUrl.indexOf("http") > -1) {
            return fileUrl;
        }
        COSClient cosClient = initUpload();
        LocalDateTime localDateTime = LocalDateTime.now().plusYears(20);
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        GeneratePresignedUrlRequest req =
                new GeneratePresignedUrlRequest(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, fileUrl, HttpMethodName.GET);
        req.setExpiration(Date.from(zdt.toInstant()));
        URL url = cosClient.generatePresignedUrl(req);
        if (url == null) {
            throw new BusinessException(BusinessExceptionEnum.URL_NOT_NULL);
        }
        return url.toString();
    }


    /**
     * 删除腾讯云文件
     *
     * @param fileName
     */
    public boolean delFile(String fileName) {
        try {
            if (fileName.indexOf("http") > -1) {
                fileName = fileName.substring(fileName.indexOf("/", 9) + 1, fileName.indexOf("?"));
            }
            COSClient cosClient = initUpload();
            cosClient.deleteObject(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO, fileName);
        } catch (CosClientException clientException) {
            clientException.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 批量删除文件
     * 1 完全删除 2删除部分 0发生错误异常
     *
     * @param fileNames 文件名称
     * @return
     */
    public List<DeletedObject> batchDelFile(List<String> fileNames) {
        COSClient cosClient = initUpload();
        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(FileUploadConfig.COS_BUCKET_GUANGZHOU_FEN_ZHI_DAO);
        ArrayList<KeyVersion> keyList = new ArrayList<>();
        fileNames.forEach(e -> {
            if (e.indexOf("http") > -1) {
                e = e.substring(e.indexOf("/", 9) + 1, e.indexOf("?"));
            }
            keyList.add(new KeyVersion(e));
        });
        deleteObjectsRequest.setKeys(keyList);
        try {
            DeleteObjectsResult deleteObjectsResult = cosClient.deleteObjects(deleteObjectsRequest);
            List<DeletedObject> deleteObjectResultArray = deleteObjectsResult.getDeletedObjects();
            return deleteObjectResultArray;
        } catch (MultiObjectDeleteException mde) { // 如果部分删除成功部分失败, 返回MultiObjectDeleteException
            List<DeletedObject> deleteObjects = mde.getDeletedObjects();
            List<DeleteError> deleteErrors = mde.getErrors();
            deleteErrors.forEach(e -> log.info("删除文件出现错误,状态码===>{}文件名====>{}错误消息====>{}版本号====>{}", e.getCode(), e.getKey(), e.getMessage(), e.getVersionId()));
            return deleteObjects;
        } catch (CosServiceException e) { // 如果是其他错误，例如参数错误， 身份验证不过等会抛出 CosServiceException
            e.printStackTrace();
        } catch (CosClientException e) { // 如果是客户端错误，例如连接不上COS
            e.printStackTrace();
        }
        return new ArrayList<>();
    }


}
